#include "config.h"

#define DBG_SUBSYS S_LIBREPLICA

#include "lich_api.h"

#include "diskmd.h"

#define USE_TASK_QUEUE 0

// externs

int diskmd_set_disklost_with_lock(disk_t *disk, const char *why);


typedef struct {
        struct list_head hook;
        int event;
        disk_t *disk;
        char reason[MAX_NAME_LEN];
        int ret;
} diskmd_async_task_t;

#if USE_TASK_QUEUE

typedef struct {
        sem_t sem;
        sy_rwlock_t lock;
        count_list_t task_list;
} diskmd_async_t;

static diskmd_async_t *__diskmd_async__ = NULL;

#endif

static int __diskmd_async_task_create(diskmd_async_task_t **task, disk_t *disk, const char *reason)
{
        int ret;
        diskmd_async_task_t *_task;

        *task = NULL;

        ret = ymalloc((void **)&_task, sizeof(diskmd_async_task_t));
        if (unlikely(ret))
                GOTO(err_ret, ret);

        strcpy(_task->reason, reason);
        _task->disk = disk;
        _task->ret = 0;

        *task = _task;
        return 0;
err_ret:
        return ret;
}

void *__do_disk_task(void *arg)
{
        int ret;
        diskmd_async_task_t *task = arg;

        task->ret = 0;

        ret = diskmd_set_disklost_with_lock(task->disk, task->reason);
        if (unlikely(ret)) {
                task->ret = ret;
                GOTO(err_ret, ret);
        }

        yfree((void **)&task);
        return NULL;
err_ret:
        DWARN("disk %d reason %s ret %d\n", task->disk->idx, task->reason, ret);
        yfree((void **)&task);
        return NULL;
}

#if USE_TASK_QUEUE

static int __diskmd_async_create(diskmd_async_t **diskmd_async)
{
        int ret;
        diskmd_async_t *_diskmd_async = NULL;

        *diskmd_async = NULL;

        ret = ymalloc((void **)&_diskmd_async, sizeof(diskmd_async_t));
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = sem_init(&_diskmd_async->sem, 0, 0);
        if (unlikely(ret))
                GOTO(err_free, ret);

        ret = sy_rwlock_init(&_diskmd_async->lock, "diskmd_async");
        if (unlikely(ret))
                YASSERT(0);

        count_list_init(&_diskmd_async->task_list);

        *diskmd_async = _diskmd_async;
        return 0;
err_free:
        yfree((void **)&_diskmd_async);
err_ret:
        return ret;
}

static void *__diskmd_async_loop(void *arg)
{
        int ret;
        diskmd_async_t *diskmd_async = __diskmd_async__;
        struct list_head *pos, *n;
        diskmd_async_task_t *task;

        (void) arg;

        count_list_t local_list;
        count_list_init(&local_list);

        while (TRUE) {
                ret = sem_wait(&diskmd_async->sem);
                if (unlikely(ret)) {
                        continue;
                }

                DBUG("count %d\n", diskmd_async->task_list.count);

                ret = sy_rwlock_wrlock(&diskmd_async->lock);
                if (unlikely(ret))
                        YASSERT(0);

                list_for_each_safe(pos, n, &diskmd_async->task_list.list) {
                        count_list_del_init(pos, &diskmd_async->task_list);
                        count_list_add_tail(pos, &local_list);
                }

                sy_rwlock_unlock(&diskmd_async->lock);

                list_for_each_safe(pos, n, &local_list.list) {
                        task = list_entry(pos, diskmd_async_task_t, hook);

                        __do_disk_task(task);
                        if (unlikely(task->ret)) {
                                DWARN("ret %d\n", task->ret);
                        }

                        count_list_del_init(pos, &local_list);
                        yfree((void **) &task);
                }
        }

        return NULL;
}

int diskmd_async_init()
{
        int ret;


        ret = __diskmd_async_create(&__diskmd_async__);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = sy_thread_create2(__diskmd_async_loop, NULL, "diskmd_async");
        if (unlikely(ret))
                GOTO(err_free, ret);

        return 0;
err_free:
        yfree((void **)&__diskmd_async__);
err_ret:
        return ret;
}

int diskmd_async_push_disk_task(disk_t *disk, const char *reason)
{
        int ret;

        diskmd_async_t *diskmd_async = __diskmd_async__;
        diskmd_async_task_t *task;

        ret = __diskmd_async_task_create(&task, disk, reason);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = sy_rwlock_wrlock(&diskmd_async->lock);
        if (unlikely(ret))
                YASSERT(0);

        count_list_add_tail(&task->hook, &diskmd_async->task_list);

        sy_rwlock_unlock(&diskmd_async->lock);

        sem_post(&diskmd_async->sem);
        return 0;
err_ret:
        return ret;
}

#else

int diskmd_async_init()
{
        return 0;
}

int diskmd_async_push_disk_task(disk_t *disk, const char *reason)
{
        int ret;
        diskmd_async_task_t *task;

        ret = __diskmd_async_task_create(&task, disk, reason);
        if (unlikely(ret))
                GOTO(err_ret, ret);

        ret = sy_thread_create(__do_disk_task, task);
        if (unlikely(ret))
                UNIMPLEMENTED(__DUMP__);

        return 0;
err_ret:
        return ret;
}

#endif
